/**
 * @file SocketIO.cc
 * @author zziywang (zziywang@163.com)
 * @brief { life is too short to learn cpp! }
 * @version 1.0
 * @date 2022-05-07
 *
 * @copyright Copyright (c) 2022
 *
 */

#include "SocketIO.hh"
#include <errno.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

namespace snow
{
SocketIO::SocketIO(int socketFd) : socketFd_(socketFd) {}

// 读取len个字节
int SocketIO::readn(char *buf, int len)
{
    int left = len;
    char *p = buf;
    while (left > 0) {
        int ret = ::read(socketFd_, p, left);
        if (-1 == ret && errno == EINTR) { // 系统调用被中断
            continue;
        } else if (-1 == ret) { // 错误
            perror("read");
            return len - left;
        } else if (0 == ret) { // 缓冲区无数据可读
            return len - left;
        } else {
            left -= ret;
            p += ret;
        }
    }
    return len - left;
}
// 读取一个int32类型数据
// int SocketIO::readInt32(int &) {}
// 读取一行数据，即读到一个'\n'就不再读取
/**
 * recv 第四个参数设置为0时，和read功能相同。
 * 都是从内核态拷贝数据到用户态，并且把内核态相应数据移走。
 *
 * 想读取一行数据，读到\n后面添加\0，\n后面的数据是并不是脏数据，是下一行的
 * 因此我们想从内核态拷贝数据但不移走内核态相应数据。recv四参数MSG_PEEK
 * **/
int SocketIO::readLine(char *buf, int maxLen)
{
    int left = maxLen - 1; // 减去最后一位'\0'
    char *p = buf;
    int total = 0;
    while (left > 0) {
        // int ret = recvPeek(p, left);
        // if (-1 == ret) {
        //     break;
        // }

        int ret = ::recv(socketFd_, buf, left, MSG_PEEK);
        if (-1 == ret && errno == EINTR) {
            continue;
        } else if (-1 == ret) {
            perror("recv peek");
            break;
        }

        // 查找'\n'
        for (int i = 0; i < ret; ++i) {
            if (p[i] == '\n') { // 找到了，多读一位，留给'\0'
                readn(p, i + 1);
                total += (i + 1);
                p += (i + 1);
                *p = '\0';
                return total;
            }
        }
        readn(p, ret);
        left -= ret;
        p += left;
        total += ret;
    }
    *p = '\0';
    return total;
}
// 将数据从内核态中复制出来（不删除内核中复制的那部分）
int SocketIO::recvPeek(char *buf, int len)
{
    int ret;
    do {
        ret = ::recv(socketFd_, buf, len, MSG_PEEK);
    } while (-1 == ret && errno == EINTR);
    if (-1 == ret) {
        perror("recv peek");
        return -1;
    }
    return ret;
}

// 写len个字节
int SocketIO::writen(const char *buf, int len)
{
    int left = len;
    const char *p = buf;
    while (left > 0) {
        int ret = ::write(socketFd_, p, left);
        if (-1 == ret && errno == EINTR) {
            continue;
        } else if (-1 == ret) {
            perror("write");
            return len - left;
        } else if (0 == ret) {
            return len - left;
        } else {
            left -= ret;
            p += ret;
        }
    }
    return len - left;
}

// int SocketIO::writeInt32(int) {}

} // namespace snow